import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpResponseBase
} from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { catchError, mergeMap, Observable, of, takeUntil, throwError, timer } from 'rxjs';

import { environment } from '@env/environment';
import { KeycloakService } from 'keycloak-angular';
import { NzNotificationService } from 'ng-zorro-antd/notification';

import { HttpCancelService } from './http-cancel.service';

const CODEMESSAGE: { [key: number]: string } = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队（异步任务）。',
  204: '删除数据成功。',
  400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限（令牌、用户名、密码错误）。',
  403: '用户得到授权，但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除，且不会再得到的。',
  422: '当创建一个对象时，发生一个验证错误。',
  500: '服务器发生错误，请检查服务器。',
  502: '网关错误。',
  503: '服务不可用，服务器暂时过载或维护。',
  504: '网关超时。'
};

/**
 * 默认HTTP拦截器
 */
@Injectable()
export class DefaultHttpInterceptor implements HttpInterceptor {
  constructor(
    private injector: Injector,
    private readonly keycloakSrc: KeycloakService,
    private httpCancelSrc: HttpCancelService
  ) {}

  private get notification(): NzNotificationService {
    return this.injector.get(NzNotificationService);
  }

  private handleError(ev: HttpErrorResponse): void {
    if (ev.status === 401) {
      this.keycloakSrc.login();
      return;
    }

    if (ev.error instanceof Blob && ev.error.type === 'application/json') {
      // Blob error
      let reader = new FileReader();
      reader.onload = (e: any) => {
        const error = JSON.parse(e.target.result);
        this.notification.error('请求异常', error?.message);
      };
      timer(100).subscribe(() => reader.readAsText(ev.error));

      return;
    }

    if (ev.status === 400 && typeof ev.error === 'string') {
      this.notification.error('请求异常', ev.error);
      return;
    }

    const errortext = CODEMESSAGE[ev.status] || ev.statusText;
    this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);

    console.log(ev);
  }

  private handleData(ev: HttpResponseBase, req: HttpRequest<any>, next: HttpHandler): Observable<any> {
    // 返回数据统一处理
    return of(ev);
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // 统一加上服务端前缀
    let url = req.url;
    if (!url.startsWith('https://') && !url.startsWith('http://')) {
      const { baseUrl } = environment.api;
      url = baseUrl + (baseUrl.endsWith('/') && url.startsWith('/') ? url.substring(1) : url);
    }

    const newReq = req.clone({ url });
    return next.handle(newReq).pipe(
      mergeMap(ev => {
        // 统一对请求处理
        if (ev instanceof HttpResponseBase) {
          return this.handleData(ev, newReq, next);
        }
        // 若一切都正常，则后续操作
        return of(ev);
      }),
      // 统一对请求错误处理
      catchError(error => {
        this.handleError(error);
        return throwError(() => error);
      }),
      takeUntil(this.httpCancelSrc.onCancelPendingRequests())
    );
  }
}
